package code.generation;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import code.parser.Clazz;
import code.parser.Method;
import code.parser.NameSpace;
import code.parser.Snippet;
import code.parser.Struct;
import code.parser.StructToClass;

public class CodeGeneration {
	public static String COPYRIGHT = "/* ******************************** */\n"
			+ "/* Code Generation By @struct2class */\n"
			+ "/* ******************************** */\n";
	public static File currentClassPath = new File(
			"D:/git/Struct2Class/struct2class/src");

	public static void main(String[] args) throws IOException {
		// genBean(null);
		parseStruct(new File(currentClassPath, "MatchVSDefines.h"), new File(
				currentClassPath, "/matchvs"), Snippet.JAVA,
				new FieldSerializeJava());
		parseClass(new File(currentClassPath, "MatchVSEngine.h").toString(),
				new File(currentClassPath, "/matchvs"), Snippet.JAVA,
				new MethodSerializeJsonJava());
		parseClass(new File(currentClassPath, "MatchVSResponse.h").toString(),
				new File(currentClassPath, "/matchvs"), Snippet.JAVA,
				new MethodSerializeJsonJava());

	}

	public static String INDENTATION_CHAR = "    ";

	public static void genBean(String[] args) throws IOException {

		System.out.println(currentClassPath.getAbsolutePath());
		StructToClass.struct2class(new File(currentClassPath,
				"MatchVSDefines.h"), new File(currentClassPath, "/cs"),
				Snippet.CS, "MatchVS");
		StructToClass.struct2class(new File(currentClassPath,
				"MatchVSDefines.h"), new File(currentClassPath, "/matchvs"),
				Snippet.JAVA, "matchvs");
	}

	private static void parseStruct(File sourceString, File outFileDir,
			String tragetSourceSuffix, FieldSerializeJava fieldSerializeJava)
			throws FileNotFoundException, IOException {
		Map<String, String> defineMap = Struct.getAllDefine(sourceString);
		defineMap.put("Int32", "int");
		if (tragetSourceSuffix.equals(".cs")) {
			defineMap.put("MsString", "string");
		} else {
			defineMap.put("MsString", "String");
			defineMap.put("bool", "boolean");
		}

		List<NameSpace> namespaceList = new NameSpace()
				.regularExpressionMatch(NameSpace.readSourcefile(sourceString));

		if (!outFileDir.exists()) {
			outFileDir.mkdirs();
		}
		for (int i = 0; i < namespaceList.size(); i++) {
			final Struct s = new Struct(namespaceList.get(i).source);
			s.defineMap = defineMap;
			s.namespace = namespaceList.get(i).name;
			List<Struct> list = s.regularExpressionMatch(s.source);
			for (Struct struct2 : list) {
				struct2.namespace = namespaceList.get(i).name;
				final String serialize = new FieldSerializeJava()
						.serialize(struct2);
				struct2.targetSource = serialize;
				System.out.println(struct2.targetSource);
				StringBuilder sb = new StringBuilder();
				sb.append(COPYRIGHT);
				sb.append(struct2.targetSource);
				wirteSourceFile(outFileDir, tragetSourceSuffix,
						sb.toString(), struct2.name);
			}
		}
	}

	/**
	 * 解析.h文件.生成目标语言的类中信息
	 * 
	 * @param pathname
	 *            .h文件路径
	 * @param outFileDir
	 *            输出路径
	 * @param classFileSuffix
	 *            目标文件名后缀.用于区分生成和何种语言 {@link #CS} {@link #JAVA}
	 * @param methodSerialize
	 *            RPC调用代码生成器
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static void parseClass(final String pathname, File outFileDir,
			String classFileSuffix, MethodSerialize methodSerialize)
			throws FileNotFoundException, IOException {
		// 定义一些语言特有的宏.替换关键字
		Map<String, String> defineMap = new HashMap<String, String>();
		if (classFileSuffix.equals(Snippet.CS)) {
			defineMap.put("virtual", "virtual");
			defineMap.put("const", "const");
			defineMap.put("namespace", "namespace");
			defineMap.put("&", " ref ");
			defineMap.put("MsString", "string");
			defineMap.put("*", "[]");
		} else {
			defineMap.put("virtual", "abstract");
			defineMap.put("const", "final");
			defineMap.put("namespace", "package");
			defineMap.put("&", "");
			defineMap.put("MsString", "String");
			defineMap.put("*", "[]");
		}
		StringBuilder sb = new StringBuilder();
		List<NameSpace> namespaceList = new NameSpace()
				.regularExpressionMatch(NameSpace.readSourcefile(new File(
						pathname)));
		for (int i = 0; i < namespaceList.size(); i++) {
			final NameSpace namespace = namespaceList.get(i);
			sb.append(COPYRIGHT);
			// gen namespace code
			sb.append(defineMap.get("namespace") + " " + namespace.name
					+ "; \n\n\n");

			List<Clazz> classList = new Clazz()
					.regularExpressionMatch(namespace.snippet);
			for (int j = 0; j < classList.size(); j++) {
				final Clazz clazz = classList.get(j);
				clazz.namespace = namespace.name;
				final String className = clazz.name;
				sb.append(methodSerialize.include(clazz));
				sb.append("public " + (clazz.isAbstractClass ? "abstract" : "")
						+ " class " + className + "{ \n\n");
				List<Method> methodList = new Method()
						.regularExpressionMatch(clazz.snippet);
				if (!clazz.isAbstractClass) {
					for (int k = 0; k < methodList.size(); k++) {
						final Method method = methodList.get(k);
						sb.append(methodSerialize.sign(method));
						sb.append(methodSerialize.call(method));
					}
				}else{
					StringBuilder callMethod = new StringBuilder();
					for (int k = 0; k < methodList.size(); k++) {
						final Method method = methodList.get(k);
						sb.append(methodSerialize.sign(method));
						sb.append(";");
						sb.append(Serialize.LINE_CHAR);
						callMethod.append(methodSerialize.callback(method));
					}
					sb.append(String.format(methodSerialize.callbackSign(),callMethod.toString()));
				}

				sb.append("\n}");

				wirteSourceFile(outFileDir, classFileSuffix, sb.toString(),
						className);
			}
		}
	}

	public static void wirteSourceFile(File outFileDir, String classFileSuffix,
			String sb, final String className) throws IOException,
			FileNotFoundException {
		if (!outFileDir.exists()) {
			outFileDir.mkdirs();
		}
		final File file = new File(outFileDir, className + classFileSuffix);
		if (!file.exists()) {
			file.createNewFile();
		}
		FileOutputStream fos = new FileOutputStream(file);
		fos.write(sb.toString().getBytes());
		fos.flush();
		fos.close();
	}
}
